package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/big"
	"os"
	"strconv"
	"strings"

	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/rpc"
)

// parameters here
var (
	// rpc address and port
	ethrpc   = ""
	ethver   = 2 // 0 - native, 1 - alias, 2 - native-no-sign, 3 - alias-no-sign
	txtype   = 0 // 0 - transfer, 1 - sccall
	jsonDir  = ""
	gasHex   = "0x1863C"
	tr_files = [4]string{"tranfer_origin.json", "tranfer_alias.json", "tranfer_origin.json", "tranfer_alias_bls.json"}
	//sc_files = [4]string{"sm_sccall_native1.json", "sm_sccall_conv_id1.json", "sm_sccall_ft1.json", "sm_sccall_ft_id1.json"}
	sc_files = [4]string{"sc_origin.json", "sc_alias.json", "sc_origin.json", "sc_alias_bls.json"}
)

type txpool struct {
	Pending hexutil.Uint
	Queued  hexutil.Uint
}

func Decstr2Hexstr(ds string) string {
	if ds == "0" {
		return "0x0"
	}
	bi, ok := new(big.Int).SetString(ds, 10)
	if !ok {
		// should consider e+
		dotpos := strings.Index(ds, ".")
		epos := strings.Index(ds, "e+")
		if epos == -1 {
			panic("cannot transform value to big.Int")
		}
		zero, _ := strconv.Atoi(ds[epos+2:])
		var newds string
		if dotpos != -1 {
			newds = ds[:dotpos] + ds[dotpos+1:epos] + strings.Repeat("0", zero-(epos-dotpos-1))
		} else {
			newds = ds[:epos] + strings.Repeat("0", zero)
		}
		bi, ok = new(big.Int).SetString(newds, 10)
		if !ok {
			panic("cannot transform value to big.Int")
		}
	}
	return fmt.Sprintf("0x%x", bi)
}

// return transaction data
func jsonReader(findex int) []interface{} {
	var fp string
	if txtype == 0 {
		fp = tr_files[findex]
	} else {
		fp = sc_files[findex]
	}
	// read file into bytes
	f, err := os.Open(jsonDir + fp)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	data, err := ioutil.ReadAll(f)
	if err != nil {
		panic(err)
	}

	// var res map[string]interface{}
	// // init a txs slice to cover json data
	// if err = json.Unmarshal(data, &res); err != nil {
	// 	panic(err)
	// }

	// if txs, ok := res["txs"]; ok {
	// 	return txs.([]interface{})
	// }
	// return nil
	var res []interface{}
	if err = json.Unmarshal(data, &res); err != nil {
		panic(err)
	}

	return res
}

func main() {
	// read env
	if len(os.Args) < 5 {
		fmt.Println("at least 4 args: <rpc> <jsondir> <ethver> <txtype>")
		fmt.Println("<ethver>:  0 - native, 1 - alias, 2 - native-no-sign, 3 - alias-no-sign")
		fmt.Println("<txtype>:  0 - transfer, 1 - sccall")
		return
	}
	ethrpc = os.Args[1]
	jsonDir = os.Args[2]
	var err error
	ethver, err = strconv.Atoi(os.Args[3])
	if err != nil {
		fmt.Printf("ethver wrong: %s\n", err)
		return
	}
	txtype, err = strconv.Atoi(os.Args[4])
	if err != nil {
		fmt.Printf("txtype wrong: %s\n", err)
		return
	}

	// connect the client
	client, err := rpc.Dial(ethrpc)
	if err != nil {
		fmt.Printf("No client listens at %s\n", ethrpc)
		os.Exit(1)
	}

	// get sender account
	var accounts []string
	if err = client.Call(&accounts, "eth_accounts"); err != nil {
		panic(err)
	}
	sender := accounts[0]

	// get json data
	if txsSlice := jsonReader(ethver); txsSlice != nil {
		// send txs
		var txhash string
		for _, tx := range txsSlice {
			txSent := tx.(map[string]interface{})
			// should deal with from / gas / value
			txSent["from"] = sender
			txSent["gas"] = gasHex
			txSent["gasPrice"] = "0x0"
			txSent["value"] = Decstr2Hexstr(txSent["value"].(string))
			if err := client.Call(&txhash, "eth_sendTransaction", txSent); err != nil {
				panic(err)
			}
			fmt.Println(txhash)
		}
	}

	// check txpool status
	var pool txpool
	client.Call(&pool, "txpool_status")
	if pool.Pending < 1000 {
		panic("somethings wrong in sending transfer transactions")
	}

	fmt.Println("Sending transactions finish")
}
